package com.mlethe.library.widget.verifycode;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.text.Editable;
import android.text.InputFilter;
import android.text.InputType;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextWatcher;
import android.text.method.PasswordTransformationMethod;
import android.text.style.UnderlineSpan;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.RelativeLayout;
import android.widget.TextView;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;

import androidx.annotation.ColorInt;
import androidx.annotation.IntDef;
import androidx.annotation.LayoutRes;
import androidx.appcompat.widget.AppCompatEditText;
import androidx.core.content.ContextCompat;

/**
 * 验证码
 *
 * @author Mlethe
 */
public class VerifyCodeView extends RelativeLayout {

    @IntDef({TEXT, NUMBER, PHONE, TEXT_PASSWORD, TEXT_VISIBLE_PASSWORD, NUMBER_PASSWORD, NUMBER_VISIBLE_PASSWORD})
    @Retention(RetentionPolicy.SOURCE)
    public @interface CodeInputType {
    }

    public static final int TEXT = 0x01;
    public static final int NUMBER = 0x02;
    public static final int PHONE = 0x03;
    public static final int TEXT_PASSWORD = 0x04;
    public static final int TEXT_VISIBLE_PASSWORD = 0x05;
    public static final int NUMBER_PASSWORD = 0x06;
    public static final int NUMBER_VISIBLE_PASSWORD = 0x07;

    private int mLength = 0;

    private EditText textEt;
    private List<TextView> mTextViews;

    private int mInputType;

    private int mLayoutId;

    private boolean isAdd = false;

    private int mFilledId, mUnfilledId;
    private Drawable mFilled, mUnfilled;

    private String mText = "";

    private OnVerifyCodeListener mOnVerifyCodeListener;

    public VerifyCodeView(Context context) {
        this(context, null);
    }

    public VerifyCodeView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public VerifyCodeView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.VerifyCodeView);
        mLayoutId = array.getResourceId(R.styleable.VerifyCodeView_verifyLayout, 0);
        mInputType = array.getInteger(R.styleable.VerifyCodeView_verifyType, NUMBER);
        mFilledId = array.getResourceId(R.styleable.VerifyCodeView_verifyFilled, 0);
        mUnfilledId = array.getResourceId(R.styleable.VerifyCodeView_verifyUnfilled, 0);
        array.recycle();
        mTextViews = new ArrayList<>();
        initLayout(context);
    }

    private void initLayout(Context context) {
        boolean isCreated = false;
        if (textEt == null) {
            isCreated = true;
            textEt = new EditText(context);
            LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
            textEt.setLayoutParams(params);
            addView(textEt);
        }
        if (mLayoutId != 0 && !isAdd) {
            inflate(context, mLayoutId, this);
            isAdd = true;
        }
        if (mFilled == null) {
            mFilled = getDrawable(mFilledId);
        }
        if (mUnfilled == null) {
            mUnfilled = getDrawable(mUnfilledId);
        }
        if (mTextViews != null) {
            mTextViews.clear();
        }
        TextView view1 = findViewById(R.id.verify_code_1);
        if (view1 != null) {
            mTextViews.add(view1);
        }
        TextView view2 = findViewById(R.id.verify_code_2);
        if (view2 != null) {
            mTextViews.add(view2);
        }
        TextView view3 = findViewById(R.id.verify_code_3);
        if (view3 != null) {
            mTextViews.add(view3);
        }
        TextView view4 = findViewById(R.id.verify_code_4);
        if (view4 != null) {
            mTextViews.add(view4);
        }
        TextView view5 = findViewById(R.id.verify_code_5);
        if (view5 != null) {
            mTextViews.add(view5);
        }
        TextView view6 = findViewById(R.id.verify_code_6);
        if (view6 != null) {
            mTextViews.add(view6);
        }
        TextView view7 = findViewById(R.id.verify_code_7);
        if (view7 != null) {
            mTextViews.add(view7);
        }
        TextView view8 = findViewById(R.id.verify_code_8);
        if (view8 != null) {
            mTextViews.add(view8);
        }

        mLength = mTextViews.size();

        if (isCreated) {
            setInputType(mInputType);

            textEt.setOnFocusChangeListener(new OnFocusChangeListener() {
                @Override
                public void onFocusChange(View v, boolean hasFocus) {
                    if (mOnVerifyCodeListener != null) {
                        mOnVerifyCodeListener.onFocusChange(textEt, hasFocus);
                        if (hasFocus) {
                            mOnVerifyCodeListener.onChanged(textEt, mText.length());
                        }
                    }
                }
            });

            /**
             * 过滤非选中输入，比如输入中文时，过滤掉搜索框里已经输入的拼音（小米手机出现的情况）
             * 返回空字符串，就代表匹配不成功，返回null代表匹配成功
             */
            textEt.setFilters(new InputFilter[]{new InputFilter() {
                @Override
                public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
//                Log.e("TAG", "filter: " + source + "|    start->" + start + "     end->" + end + "     dest->" + dest + "|    dstart->" + dstart + "       dend->" + dend + "    length->" + source.length());
                    if (source.toString().equals(" ") || source.toString().equals(" ")) {   // 去除空格
                        return "";
                    }
                    SpannableString ss = new SpannableString(source);
                    Object[] spans = ss.getSpans(0, ss.length(), Object.class);
                    if (spans != null) {
                        for (Object span : spans) {
                            if (span instanceof UnderlineSpan) {
                                // 有下划线
                                return "";
                            }
                        }
                    }
                    return source;
                }
            }, new InputFilter.LengthFilter(mLength)});

            textEt.addTextChangedListener(new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {

                }

                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
//                Log.e("TAG", "onTextChanged: s->" + s + "    start->" + start + "    before->" + before + "   count->" + count);
                    if (mOnVerifyCodeListener != null && mLength > s.length()) {
                        mOnVerifyCodeListener.onChanged(textEt, s.length());
                    }
                }

                @Override
                public void afterTextChanged(Editable s) {
                    mText = s.toString().trim();

                    int length = mText.length();
                    for (int i = 0; i < mLength; i++) {
                        if (i < length) {
                            if (mInputType == TEXT_PASSWORD || mInputType == NUMBER_PASSWORD) {
                                mTextViews.get(i).setText("•");
                            } else {
                                mTextViews.get(i).setText(s.toString().substring(i));
                            }
                            if (mFilled != null) {
                                mTextViews.get(i).setBackground(mFilled);
                            }
                        } else {
                            mTextViews.get(i).setText("");
                            if (mUnfilled != null) {
                                mTextViews.get(i).setBackground(mUnfilled);
                            }
                        }
                    }

                    if (length >= mLength) {
                        if (mOnVerifyCodeListener != null) {
                            mOnVerifyCodeListener.onComplete(mText);
                        }
                    }
                }
            });
        }
    }

    private Drawable getDrawable(int resId) {
        if (resId != 0) {
            return ContextCompat.getDrawable(getContext(), resId);
        }
        return null;
    }

    /**
     * 清除内容
     */
    public void clearText() {
        if (textEt != null) {
            textEt.setText("");
        }
    }

    /**
     * 获取输入内容
     *
     * @return
     */
    public String getText() {
        return mText;
    }

    /**
     * 设置布局
     *
     * @param layoutId
     * @return
     */
    public VerifyCodeView setLayoutId(@LayoutRes int layoutId) {
        this.mLayoutId = layoutId;
        removeAllViews();
        textEt = null;
        isAdd = false;
        initLayout(getContext());
        return this;
    }

    /**
     * 设置文字大小
     *
     * @param sp
     */
    public VerifyCodeView setTextSize(float sp) {
        if (mTextViews != null) {
            for (TextView textView : mTextViews) {
                textView.setTextSize(sp);
            }
        }
        return this;
    }

    /**
     * 设置文字颜色
     *
     * @param color
     */
    public VerifyCodeView setTextColor(@ColorInt int color) {
        if (mTextViews != null) {
            for (TextView textView : mTextViews) {
                textView.setTextColor(color);
            }
        }
        return this;
    }

    /**
     * 已填充样式
     *
     * @param resId
     */
    public VerifyCodeView setFilledId(int resId) {
        recycleDrawable(mFilled);
        mFilled = null;
        this.mFilledId = resId;
        mFilled = getDrawable(mFilledId);
        return this;
    }

    /**
     * 未填充样式
     *
     * @param resId
     */
    public VerifyCodeView setUnfilledId(int resId) {
        recycleDrawable(mUnfilled);
        mUnfilled = null;
        this.mUnfilledId = resId;
        mUnfilled = getDrawable(mUnfilledId);
        return this;
    }

    /**
     * 已填充样式
     *
     * @param filled
     */
    public VerifyCodeView setFilled(Drawable filled) {
        recycleDrawable(mFilled);
        mFilled = null;
        this.mFilled = filled;
        return this;
    }

    /**
     * 未填充样式
     *
     * @param unfilled
     */
    public VerifyCodeView setUnfilled(Drawable unfilled) {
        recycleDrawable(mUnfilled);
        mUnfilled = null;
        this.mUnfilled = unfilled;
        return this;
    }

    public EditText getEditText() {
        return textEt;
    }

    public VerifyCodeView setImeOptions(int imeOptions) {
        if (textEt != null) {
            textEt.setImeOptions(imeOptions);
        }
        return this;
    }

    public VerifyCodeView setOnEditorActionListener(TextView.OnEditorActionListener listener) {
        if (textEt != null) {
            textEt.setOnEditorActionListener(listener);
        }
        return this;
    }

    public VerifyCodeView setInputType(@CodeInputType int type) {
        mInputType = type;
        if (textEt != null) {
            switch (type) {
                case TEXT:
                    textEt.setInputType(InputType.TYPE_CLASS_TEXT);
                    break;
                case NUMBER:
                    textEt.setInputType(InputType.TYPE_CLASS_NUMBER);
                    break;
                case PHONE:
                    textEt.setInputType(InputType.TYPE_CLASS_PHONE);
                    break;
                case TEXT_PASSWORD:
                    textEt.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
                    textEt.setTypeface(Typeface.DEFAULT);
                    textEt.setTransformationMethod(new PasswordTransformationMethod());
                    //隐藏密码
                    textEt.setTransformationMethod(PasswordTransformationMethod.getInstance());
                    break;
                case TEXT_VISIBLE_PASSWORD:
                    textEt.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
//                textEt.setTypeface(Typeface.DEFAULT);
//                textEt.setTransformationMethod(new PasswordTransformationMethod());
                    //明文显示密码
//                textEt.setTransformationMethod(HideReturnsTransformationMethod.getInstance());
                    break;
                case NUMBER_PASSWORD:
                    textEt.setInputType(InputType.TYPE_CLASS_NUMBER);
                    textEt.setTypeface(Typeface.DEFAULT);
                    textEt.setTransformationMethod(new PasswordTransformationMethod());
                    //隐藏密码
                    textEt.setTransformationMethod(PasswordTransformationMethod.getInstance());
                    break;
                case NUMBER_VISIBLE_PASSWORD:
                    textEt.setInputType(InputType.TYPE_CLASS_NUMBER);
//                textEt.setTypeface(Typeface.DEFAULT);
//                textEt.setTransformationMethod(new PasswordTransformationMethod());
                    //明文显示密码
//                textEt.setTransformationMethod(HideReturnsTransformationMethod.getInstance());
                    break;
            }
        }
        return this;
    }

    public VerifyCodeView setOnVerifyCodeListener(OnVerifyCodeListener listener) {
        this.mOnVerifyCodeListener = listener;
        return this;
    }

    /**
     * 释放drawable
     *
     * @param drawable
     */
    private void recycleDrawable(Drawable drawable) {
        if (drawable != null) {
            if (drawable instanceof BitmapDrawable) {
                Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
                if (bitmap != null && !bitmap.isRecycled()) {
                    bitmap.recycle();
                    bitmap = null;
                }
            }
        }
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        initLayout(getContext());
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        textEt = null;
        recycleDrawable(mFilled);
        mFilled = null;
        recycleDrawable(mUnfilled);
        mUnfilled = null;
        if (mTextViews != null) {
            mTextViews.clear();
        }
    }

    public interface OnVerifyCodeListener {
        void onFocusChange(EditText editText, boolean hasFocus);

        void onChanged(EditText editText, int index);

        void onComplete(String text);
    }

    public class EditText extends AppCompatEditText {

        private long lastTime = 0;

        public EditText(Context context) {
            this(context, null);
        }

        public EditText(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }

        public EditText(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            setFocusable(true);
            setFocusableInTouchMode(true);
//            setTextColor(Color.TRANSPARENT);
//            setBackgroundColor(Color.TRANSPARENT);
            setAlpha(0);
            setCursorVisible(false);
            setImeOptions(EditorInfo.IME_ACTION_DONE);
        }

        @Override
        protected void onSelectionChanged(int selStart, int selEnd) {
            super.onSelectionChanged(selStart, selEnd);
            //把光标位置固定在最末
            this.setSelection(this.getText().length());
        }

        @Override
        public boolean onTouchEvent(MotionEvent event) {
            //屏蔽双击事件
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    long currentTime = System.currentTimeMillis();
                    if (currentTime - lastTime < 500) {
                        lastTime = currentTime;
                        return true;
                    } else {
                        lastTime = currentTime;
                    }
                    break;
            }
            return super.onTouchEvent(event);
        }
    }
}
